/*
 * Copyright 2004-2007 Gary Bentley 
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may 
 * not use this file except in compliance with the License. 
 * You may obtain a copy of the License at 
 *    http://www.apache.org/licenses/LICENSE-2.0 
 *
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 * See the License for the specific language governing permissions and 
 * limitations under the License.
 */
package org.sourceprojects.cappadocia.org.josql.functions.regexp;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import org.sourceprojects.cappadocia.org.josql.Query;
import org.sourceprojects.cappadocia.org.josql.QueryExecutionException;

public class ApacheRegExpWrapper extends AbstractRegExpWrapper implements
		RegExp {

	public static final String SUPPORTED_VERSION = "1.3";

	private final String compilerClassName = "org.apache.regexp.RECompiler";
	private final String matcherClassName = "org.apache.regexp.RE";
	private final String programClassName = "org.apache.regexp.REProgram";
	private final String compileMethName = "compile";
	private final String setProgramMethName = "setProgram";
	private final String matchMethName = "match";

	private Method compileMeth = null;
	private Method setProgramMeth = null;
	private Method matchMeth = null;
	private Class compilerClass = null;
	private Class matcherClass = null;

	private final Map patterns = new HashMap();

	public ApacheRegExpWrapper() {

	}

	@Override
	public String getSupportedVersion() {

		return ApacheRegExpWrapper.SUPPORTED_VERSION;

	}

	@Override
	public boolean isAvailable(final Query q) {

		try {

			q.loadClass(this.compilerClassName);

			return true;

		} catch (final Exception e) {

			return false;

		}

	}

	@Override
	public void init(final Query q) throws QueryExecutionException {

		try {

			this.compilerClass = q.loadClass(this.compilerClassName);

			final Class argTypes[] = { String.class };

			this.compileMeth = this.compilerClass.getMethod(
					this.compileMethName, argTypes);

			this.matcherClass = q.loadClass(this.matcherClassName);

			final Class pc = q.loadClass(this.programClassName);

			final Class argTypes2[] = { pc };

			this.setProgramMeth = this.matcherClass.getMethod(
					this.setProgramMethName, argTypes2);

			this.matchMeth = this.matcherClass.getMethod(this.matchMethName,
					argTypes);

		} catch (final Exception e) {

			throw new QueryExecutionException("Unable to init", e);

		}

	}

	@Override
	public boolean match(final String pattern, final String val)
			throws QueryExecutionException {

		try {

			// See if we already have the pattern.
			Object o = this.patterns.get(pattern);

			if (o == null) {

				// Create a new compiler.
				final Object c = this.compilerClass.newInstance();

				final Object args[] = { pattern };

				// Compile the pattern.
				final Object program = this.compileMeth.invoke(c, args);

				// Create a new RE.
				final Object re = this.matcherClass.newInstance();

				final Object args2[] = { program };

				// Apply the program.
				this.setProgramMeth.invoke(re, args2);

				this.patterns.put(pattern, re);

				o = re;

			}

			final Object args[] = { val };

			// Now try and match the value.
			return ((Boolean) this.matchMeth.invoke(o, args)).booleanValue();

		} catch (final Exception e) {

			throw new QueryExecutionException("Unable to match value: " + val
					+ " against pattern: " + pattern, e);

		}

	}

}
